import fileinput
import operator
class HybridWeight(object):
    def __init__(self):
        self.dic = {}
        self.accuracy_dic={}
        self.recommendation_dic={}
        self.count=0
        self.average=100
        self.coverage=100
        self.recommendations=0
        self.correct=-1
        
    def train(self):
        for session in fileinput.input(['D:\\train_d.csv']):
            try:
                session= session.strip().rstrip(',')
                session=session.split(',')
                length=len(session)-1
                if self.dic.has_key(length):
                    dic_tree=self.dic.get(length)
                    dic_tree.add(session)
                else:
                    tree=HybridTree()
                    tree.add(session)
                    self.dic[length]=tree
                    self.accuracy_dic[length]=0
            except:
                continue
        print "Finished"
        
    def continous_train(self,line):
        line= line.strip().rstrip(',')
        line=line.split(',')
        length=len(line)-1
        if self.dic.has_key(length):
            dic_tree=self.dic.get(length)
            dic_tree.add(line)
        else:
            tree=HybridTree()
            tree.add(line)
            self.dic[length]=tree
            self.accuracy_dic[length]=0
    def recommend(self,session):
        templist=[]
        rtemplist=[]
        result=[]
        self.recommendation_dic={}
        self.recommendations+=1
        for r in reversed(session):
            templist.append(r)
            rtemplist=templist
            rtemplist.reverse()
            length=len(templist)
            if self.dic.has_key(length):
                dic_tree=self.dic.get(length)
                prediction = dic_tree.checkPath(rtemplist)
                if prediction:
                    prediction.sort(key=lambda x: x._count, reverse=True)
                    if len(prediction)>20:
                        self.recommendation_dic[length]=prediction[0:20]
                    else:
                        self.recommendation_dic[length]=prediction[0:]
        dictionary=self.recommenddomiansdic()
        if len(dictionary)>0:
            self.count+=1
            result=sorted(dictionary.items(), key=lambda x: x[1], reverse=True)
            result_list=self.predict_list(result)
        return result_list
     
    def evaluate(self,recommendationlist,actual):
        self.correct=0
        node =HybridNode(None,actual)
        length =len(recommendationlist)
        if actual in recommendationlist:
            self.correct=1
            self.average = self.calculateAccuracy(self.correct,self.average,self.count)
            for key,value in recommendationdic.iteritems():
                if node in value:
                    self.accuracy_dic[key]=self.calculateAccuracy(1,self.accuracy_dic[key],self.count)
                else:
                    self.accuracy_dic[key]=self.calculateAccuracy(0,self.accuracy_dic[key],self.count)   
            return (self.average,self.count,self.correct,self.accuracy_dic)
        else:
            self.correct=0
            self.average=self.calculateAccuracy(self.correct,self.average,self.count)
            self.accuracy_dic=self.decreaseAccuracy() 
            return (self.average,self.count,self.correct,self.accuracy_dic)          
    
    def decreaseAccuracy(self):
        for key,value in self.accuracy_dic.iteritems():
            self.accuracy_dic[key]=self.calculateAccuracy(0,self.accuracy_dic[key],self.count)  
        return self.accuracy_dic
    
    def recommenddomiansdic(self):
        domaindic={}
        for key,value in self.recommendation_dic.iteritems():
            for j in value:
                if not domaindic.has_key(j._item):
                    domaindic[j._item]=self.calculateWeight(key,j._count)
                else:
                    domaindic[j._item]+=self.calculateWeight(key,j._count)
        return domaindic
    
    def calculateWeight(self,key,count):
        weight=count*float(self.accuracy_dic[key])
        return weight
    
    def predict_list(self,result):
        result_list=[]
        for element in result:
            result_list.append(element[0])
        return result_list
    
    def calculateAccuracy(self,right,maverage,mcount):
        return ((int(mcount)-1)*float(maverage)+int(right))/int(mcount)
    
    def getCoverage(self):
        if not self.recommendations == 0:
            self.coverage=float(float(self.count)*100/float(self.recommendations))
        else:
            self.coverage=0
        return self.coverage
    
    def getAccuracy(self):
        return self.average*100
    
    def getCorrect(self):
        return self.correct
    def getCount(self):
        return self.count

class HybridTree(object):
    def __init__(self):
        self._root = HybridNode(self, -1, None)

    @property
    def root(self):
        return self._root

    def add(self, transaction):
        point = self._root

        for item in transaction:
            next_point = point.search(item)
            if next_point:
                next_point.increment()
            else:
                next_point = HybridNode(self, item)
                point.add(next_point)
            point = next_point

    def printTree(self, root1):
        print "Root ", root1._item
        print root1._children.values()
        for x in root1._children.values():
            self.printTree(x)
            
    def checkPath(self,sessionlist):
        sessionlist.reverse()
        point = self._root
        for i in sessionlist:
            node = point.search(i)
            if node:
                point=node
            else:
                return None
        return point._children.values()
               

class HybridNode(object):
    def __init__(self, tree, item, count=1):
        self._tree = tree
        self._item = item
        self._parent = None
        self._children = {}
        self._neighbor = None
        self._count = count

    def add(self, child):
        if not isinstance(child, HybridNode):
            raise TypeError("Can only add other Node as children")

        if not child.item in self._children:
            self._children[child.item] = child
            child.parent = self

    def search(self, item):
        try:
            return self._children[item]
        except KeyError:
            return None
        
    def __eq__(self, other):
        return self._item==other._item
    
    def __hash__(self):
        return hash(self._item)

    def __contains__(self, item):
        return item in self._children

    @property
    def tree(self):
        """The tree in which this node appears."""
        return self._tree

    @property
    def item(self):
        """The item contained in this node."""
        return self._item

    @property
    def count(self):
        """The count associated with this node's item."""
        return self._count

    def increment(self):
        """Increments the count associated with this node's item."""
        if self._count is None:
            raise ValueError("Root nodes have no associated count.")
        self._count += 1

    @property
    def root(self):
        """True if this node is the root of a tree; false if otherwise."""
        return self._item is None and self._count is None

    @property
    def leaf(self):
        """True if this node is a leaf in the tree; false if otherwise."""
        return len(self._children) == 0

    @property
    def children(self):
        """The nodes that are children of this node."""
#        print self._children.items()
#        return 1
        return tuple(self._children.itervalues())
#        return list(self._children.itervalues())

    def inspect(self, depth=0):
        print ('  ' * depth) + repr(self)
        for child in self.children:
            child.inspect(depth + 1)

    def __repr__(self):
        if self.root:
            return "<%s (root)>" % type(self).__name__
        return "<%s %r (%r)>" % (type(self).__name__, self.item, self.count)

if __name__ == "__main__": 
    aw=HybridWeight()
    aw.train()
    for line in fileinput.input(['D:\\python\\DATSET\\3daysdata\\D\\test_d.csv']):
               try:
                    list2=[]
                    line2= line.strip().rstrip(',')
                    line3=line2.split(',')
                    length=len(line3)
                    if length==1:
                        continue
                    for i in range(0,length-1):
                        patial_session=line3[0:i+1]
                        recomendation_list_aw=aw.recommend(patial_session)
                        aw.evaluate(recomendation_list_aw, line3[i+1])
                    aw.continous_train(line)
               except:
                    continue
    print "Accu : ",aw.getAccuracy()
    print "Cover : ",aw.getCoverage()
    print "Count : ",aw.getCount()

